"""
交易风控管理模块
================

提供实时风控检查、风险评估和风控规则管理功能
确保交易系统的安全性和合规性
"""

# 确保项目根目录在Python路径中

# 统一路径管理
from backend.shared import PathManager
PathManager().setup_paths()
import sys
import os

import asyncio
import logging
from typing import Dict, List, Optional, Any, Tuple
from decimal import Decimal
from datetime import datetime, timedelta
from enum import Enum
import json

class RiskLevel(Enum):
    """风险等级"""
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    CRITICAL = "critical"

class RiskType(Enum):
    """风险类型"""
    POSITION_LIMIT = "position_limit"
    DAILY_LOSS_LIMIT = "daily_loss_limit"
    SINGLE_ORDER_LIMIT = "single_order_limit"
    FREQUENCY_LIMIT = "frequency_limit"
    CAPITAL_ADEQUACY = "capital_adequacy"
    CONCENTRATION_RISK = "concentration_risk"
    VOLATILITY_RISK = "volatility_risk"
    LIQUIDITY_RISK = "liquidity_risk"
    CREDIT_RISK = "credit_risk"
    OPERATIONAL_RISK = "operational_risk"

class RiskCheckResult:
    """风控检查结果"""
    
    def __init__(self, passed: bool, risk_type: Optional[RiskType] = None,
                 risk_level: Optional[RiskLevel] = None, message: str = ""):
        self.passed = passed
        self.risk_type = risk_type
        self.risk_level = risk_level
        self.message = message
        self.timestamp = datetime.utcnow()
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "passed": self.passed,
            "risk_type": self.risk_type.value if self.risk_type else None,
            "risk_level": self.risk_level.value if self.risk_level else None,
            "message": self.message,
            "timestamp": self.timestamp.isoformat()
        }

class RiskRule:
    """风控规则基类"""
    
    def __init__(self, rule_id: str, name: str, risk_type: RiskType,
                 enabled: bool = True, priority: int = 0):
        self.rule_id = rule_id
        self.name = name
        self.risk_type = risk_type
        self.enabled = enabled
        self.priority = priority
        self.logger = logging.getLogger(f"{__name__}.{rule_id}")
    
    async def check(self, user_id: str, order_data: Dict[str, Any],
                   context: Dict[str, Any]) -> RiskCheckResult:
        """
        执行风控检查
        
        Args:
            user_id: 用户ID
            order_data: 订单数据
            context: 上下文数据
            
        Returns:
            风控检查结果
        """
        raise NotImplementedError
    
    def get_config(self) -> Dict[str, Any]:
        """获取规则配置"""
        return {
            "rule_id": self.rule_id,
            "name": self.name,
            "risk_type": self.risk_type.value,
            "enabled": self.enabled,
            "priority": self.priority
        }

class PositionLimitRule(RiskRule):
    """持仓限制规则"""
    
    def __init__(self, max_position_value: Decimal = Decimal('1000000'),
                 max_position_ratio: Decimal = Decimal('0.3')):
        super().__init__("position_limit", "持仓限制", RiskType.POSITION_LIMIT)
        self.max_position_value = max_position_value
        self.max_position_ratio = max_position_ratio
    
    async def check(self, user_id: str, order_data: Dict[str, Any],
                   context: Dict[str, Any]) -> RiskCheckResult:
        """检查持仓限制"""
        try:
            # 获取当前持仓信息
            current_positions = context.get("positions", {})
            account_balance = context.get("account_balance", Decimal('0'))
            
            symbol = order_data["symbol"]
            volume = Decimal(str(order_data["volume"]))
            price = Decimal(str(order_data.get("price", 0)))
            direction = order_data["direction"]
            
            # 计算新订单的持仓价值
            order_value = volume * price
            
            # 获取当前该品种的持仓
            current_position = current_positions.get(symbol, {})
            current_volume = Decimal(str(current_position.get("volume", 0)))
            current_value = Decimal(str(current_position.get("value", 0)))
            
            # 计算新的持仓
            if direction == "LONG":
                new_volume = current_volume + volume
                new_value = current_value + order_value
            else:
                new_volume = max(Decimal('0'), current_volume - volume)
                new_value = max(Decimal('0'), current_value - order_value)
            
            # 检查持仓价值限制
            if new_value > self.max_position_value:
                return RiskCheckResult(
                    False, RiskType.POSITION_LIMIT, RiskLevel.HIGH,
                    f"持仓价值超限: {new_value} > {self.max_position_value}"
                )
            
            # 检查持仓比例限制
            if account_balance > 0:
                position_ratio = new_value / account_balance
                if position_ratio > self.max_position_ratio:
                    return RiskCheckResult(
                        False, RiskType.POSITION_LIMIT, RiskLevel.MEDIUM,
                        f"持仓比例超限: {position_ratio:.2%} > {self.max_position_ratio:.2%}"
                    )
            
            return RiskCheckResult(True, message="持仓检查通过")
            
        except Exception as e:
            self.logger.error(f"持仓限制检查异常: {e}")
            return RiskCheckResult(
                False, RiskType.POSITION_LIMIT, RiskLevel.CRITICAL,
                f"持仓检查异常: {str(e)}"
            )

class DailyLossLimitRule(RiskRule):
    """日损失限制规则"""
    
    def __init__(self, max_daily_loss: Decimal = Decimal('50000'),
                 max_loss_ratio: Decimal = Decimal('0.05')):
        super().__init__("daily_loss_limit", "日损失限制", RiskType.DAILY_LOSS_LIMIT)
        self.max_daily_loss = max_daily_loss
        self.max_loss_ratio = max_loss_ratio
    
    async def check(self, user_id: str, order_data: Dict[str, Any],
                   context: Dict[str, Any]) -> RiskCheckResult:
        """检查日损失限制"""
        try:
            # 获取今日PnL
            daily_pnl = Decimal(str(context.get("daily_pnl", 0)))
            account_balance = Decimal(str(context.get("account_balance", 0)))
            
            # 如果今日已经盈利，直接通过
            if daily_pnl >= 0:
                return RiskCheckResult(True, message="今日盈利，损失检查通过")
            
            # 检查绝对损失限制
            daily_loss = abs(daily_pnl)
            if daily_loss > self.max_daily_loss:
                return RiskCheckResult(
                    False, RiskType.DAILY_LOSS_LIMIT, RiskLevel.CRITICAL,
                    f"日损失超限: {daily_loss} > {self.max_daily_loss}"
                )
            
            # 检查损失比例限制
            if account_balance > 0:
                loss_ratio = daily_loss / account_balance
                if loss_ratio > self.max_loss_ratio:
                    return RiskCheckResult(
                        False, RiskType.DAILY_LOSS_LIMIT, RiskLevel.HIGH,
                        f"日损失比例超限: {loss_ratio:.2%} > {self.max_loss_ratio:.2%}"
                    )
            
            return RiskCheckResult(True, message="日损失检查通过")
            
        except Exception as e:
            self.logger.error(f"日损失限制检查异常: {e}")
            return RiskCheckResult(
                False, RiskType.DAILY_LOSS_LIMIT, RiskLevel.CRITICAL,
                f"日损失检查异常: {str(e)}"
            )

class SingleOrderLimitRule(RiskRule):
    """单笔订单限制规则"""
    
    def __init__(self, max_order_value: Decimal = Decimal('100000'),
                 max_order_ratio: Decimal = Decimal('0.1')):
        super().__init__("single_order_limit", "单笔订单限制", RiskType.SINGLE_ORDER_LIMIT)
        self.max_order_value = max_order_value
        self.max_order_ratio = max_order_ratio
    
    async def check(self, user_id: str, order_data: Dict[str, Any],
                   context: Dict[str, Any]) -> RiskCheckResult:
        """检查单笔订单限制"""
        try:
            volume = Decimal(str(order_data["volume"]))
            price = Decimal(str(order_data.get("price", 0)))
            account_balance = Decimal(str(context.get("account_balance", 0)))
            
            # 计算订单价值
            order_value = volume * price
            
            # 检查绝对价值限制
            if order_value > self.max_order_value:
                return RiskCheckResult(
                    False, RiskType.SINGLE_ORDER_LIMIT, RiskLevel.MEDIUM,
                    f"单笔订单价值超限: {order_value} > {self.max_order_value}"
                )
            
            # 检查订单价值比例限制
            if account_balance > 0:
                order_ratio = order_value / account_balance
                if order_ratio > self.max_order_ratio:
                    return RiskCheckResult(
                        False, RiskType.SINGLE_ORDER_LIMIT, RiskLevel.MEDIUM,
                        f"单笔订单比例超限: {order_ratio:.2%} > {self.max_order_ratio:.2%}"
                    )
            
            return RiskCheckResult(True, message="单笔订单检查通过")
            
        except Exception as e:
            self.logger.error(f"单笔订单限制检查异常: {e}")
            return RiskCheckResult(
                False, RiskType.SINGLE_ORDER_LIMIT, RiskLevel.CRITICAL,
                f"单笔订单检查异常: {str(e)}"
            )

class FrequencyLimitRule(RiskRule):
    """频率限制规则"""
    
    def __init__(self, max_orders_per_minute: int = 60,
                 max_orders_per_hour: int = 1000):
        super().__init__("frequency_limit", "频率限制", RiskType.FREQUENCY_LIMIT)
        self.max_orders_per_minute = max_orders_per_minute
        self.max_orders_per_hour = max_orders_per_hour
    
    async def check(self, user_id: str, order_data: Dict[str, Any],
                   context: Dict[str, Any]) -> RiskCheckResult:
        """检查下单频率限制"""
        try:
            redis_client = context.get("redis_client")
            if not redis_client:
                return RiskCheckResult(True, message="无Redis客户端，跳过频率检查")
            
            now = datetime.utcnow()
            minute_key = f"risk:frequency:{user_id}:{now.strftime('%Y%m%d%H%M')}"
            hour_key = f"risk:frequency:{user_id}:{now.strftime('%Y%m%d%H')}"
            
            # 检查分钟级频率
            minute_count = await redis_client.get(minute_key) or 0
            minute_count = int(minute_count)
            
            if minute_count >= self.max_orders_per_minute:
                return RiskCheckResult(
                    False, RiskType.FREQUENCY_LIMIT, RiskLevel.HIGH,
                    f"分钟下单频率超限: {minute_count} >= {self.max_orders_per_minute}"
                )
            
            # 检查小时级频率
            hour_count = await redis_client.get(hour_key) or 0
            hour_count = int(hour_count)
            
            if hour_count >= self.max_orders_per_hour:
                return RiskCheckResult(
                    False, RiskType.FREQUENCY_LIMIT, RiskLevel.MEDIUM,
                    f"小时下单频率超限: {hour_count} >= {self.max_orders_per_hour}"
                )
            
            # 更新计数器
            await redis_client.incr(minute_key)
            await redis_client.expire(minute_key, 60)  # 1分钟过期
            
            await redis_client.incr(hour_key)
            await redis_client.expire(hour_key, 3600)  # 1小时过期
            
            return RiskCheckResult(True, message="频率检查通过")
            
        except Exception as e:
            self.logger.error(f"频率限制检查异常: {e}")
            return RiskCheckResult(
                False, RiskType.FREQUENCY_LIMIT, RiskLevel.CRITICAL,
                f"频率检查异常: {str(e)}"
            )

class RiskManager:
    """风控管理器"""
    
    def __init__(self, redis_client=None):
        """
        初始化风控管理器
        
        Args:
            redis_client: Redis客户端
        """
        self.redis_client = redis_client
        self.logger = logging.getLogger(__name__)
        
        # 风控规则列表
        self.rules: List[RiskRule] = []
        
        # 统计信息
        self._check_count = 0
        self._blocked_count = 0
        self._warning_count = 0
        
        # 初始化默认规则
        self._init_default_rules()
        
        self.logger.info("风控管理器初始化完成")
    
    def _init_default_rules(self):
        """初始化默认风控规则"""
        self.add_rule(PositionLimitRule())
        self.add_rule(DailyLossLimitRule())
        self.add_rule(SingleOrderLimitRule())
        self.add_rule(FrequencyLimitRule())
    
    def add_rule(self, rule: RiskRule):
        """
        添加风控规则
        
        Args:
            rule: 风控规则
        """
        self.rules.append(rule)
        # 按优先级排序
        self.rules.sort(key=lambda x: x.priority, reverse=True)
        self.logger.info(f"添加风控规则: {rule.name}")
    
    def remove_rule(self, rule_id: str):
        """
        移除风控规则
        
        Args:
            rule_id: 规则ID
        """
        self.rules = [rule for rule in self.rules if rule.rule_id != rule_id]
        self.logger.info(f"移除风控规则: {rule_id}")
    
    def get_rule(self, rule_id: str) -> Optional[RiskRule]:
        """
        获取风控规则
        
        Args:
            rule_id: 规则ID
            
        Returns:
            风控规则
        """
        return next((rule for rule in self.rules if rule.rule_id == rule_id), None)
    
    def enable_rule(self, rule_id: str):
        """
        启用风控规则
        
        Args:
            rule_id: 规则ID
        """
        rule = self.get_rule(rule_id)
        if rule:
            rule.enabled = True
            self.logger.info(f"启用风控规则: {rule_id}")
    
    def disable_rule(self, rule_id: str):
        """
        禁用风控规则
        
        Args:
            rule_id: 规则ID
        """
        rule = self.get_rule(rule_id)
        if rule:
            rule.enabled = False
            self.logger.info(f"禁用风控规则: {rule_id}")
    
    async def check_order_risk(self, user_id: str, order_data: Dict[str, Any]) -> RiskCheckResult:
        """
        检查订单风险
        
        Args:
            user_id: 用户ID
            order_data: 订单数据
            
        Returns:
            风控检查结果
        """
        self._check_count += 1
        
        try:
            # 构建检查上下文
            context = await self._build_context(user_id, order_data)
            
            # 执行所有启用的规则检查
            for rule in self.rules:
                if not rule.enabled:
                    continue
                
                result = await rule.check(user_id, order_data, context)
                
                if not result.passed:
                    # 记录风控事件
                    await self._log_risk_event(user_id, rule, result, order_data)
                    
                    if result.risk_level in [RiskLevel.HIGH, RiskLevel.CRITICAL]:
                        self._blocked_count += 1
                        self.logger.warning(f"订单被风控拦截: {result.message}")
                        return result
                    else:
                        self._warning_count += 1
                        self.logger.warning(f"风控警告: {result.message}")
            
            # 所有检查通过
            return RiskCheckResult(True, message="风控检查通过")
            
        except Exception as e:
            self.logger.error(f"风控检查异常: {e}")
            return RiskCheckResult(
                False, RiskType.OPERATIONAL_RISK, RiskLevel.CRITICAL,
                f"风控检查异常: {str(e)}"
            )
    
    async def _build_context(self, user_id: str, order_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        构建风控检查上下文
        
        Args:
            user_id: 用户ID
            order_data: 订单数据
            
        Returns:
            上下文数据
        """
        context = {
            "redis_client": self.redis_client,
            "user_id": user_id,
            "order_data": order_data,
            "timestamp": datetime.utcnow()
        }
        
        # 这里应该从数据库或缓存获取用户的相关数据
        # 暂时使用模拟数据
        context.update({
            "account_balance": Decimal('100000'),  # 账户余额
            "daily_pnl": Decimal('-5000'),         # 日盈亏
            "positions": {},                       # 持仓信息
            "recent_orders": []                    # 最近订单
        })
        
        return context
    
    async def _log_risk_event(self, user_id: str, rule: RiskRule, 
                             result: RiskCheckResult, order_data: Dict[str, Any]):
        """
        记录风控事件
        
        Args:
            user_id: 用户ID
            rule: 触发的规则
            result: 检查结果
            order_data: 订单数据
        """
        if not self.redis_client:
            return
        
        try:
            event = {
                "user_id": user_id,
                "rule_id": rule.rule_id,
                "rule_name": rule.name,
                "risk_type": result.risk_type.value,
                "risk_level": result.risk_level.value,
                "message": result.message,
                "order_data": order_data,
                "timestamp": result.timestamp.isoformat()
            }
            
            # 记录到Redis
            await self.redis_client.lpush(
                f"risk:events:{user_id}",
                json.dumps(event, default=str)
            )
            
            # 设置过期时间（30天）
            await self.redis_client.expire(f"risk:events:{user_id}", 30 * 24 * 3600)
            
        except Exception as e:
            self.logger.error(f"风控事件记录失败: {e}")
    
    def get_statistics(self) -> Dict[str, Any]:
        """
        获取风控统计信息
        
        Returns:
            统计信息
        """
        enabled_rules = [rule for rule in self.rules if rule.enabled]
        
        return {
            "total_rules": len(self.rules),
            "enabled_rules": len(enabled_rules),
            "check_count": self._check_count,
            "blocked_count": self._blocked_count,
            "warning_count": self._warning_count,
            "block_rate": self._blocked_count / self._check_count if self._check_count > 0 else 0,
            "warning_rate": self._warning_count / self._check_count if self._check_count > 0 else 0
        }
    
    def get_rules_config(self) -> List[Dict[str, Any]]:
        """
        获取所有规则配置
        
        Returns:
            规则配置列表
        """
        return [rule.get_config() for rule in self.rules]
    
    async def get_user_risk_events(self, user_id: str, limit: int = 100) -> List[Dict[str, Any]]:
        """
        获取用户风控事件
        
        Args:
            user_id: 用户ID
            limit: 获取数量限制
            
        Returns:
            风控事件列表
        """
        if not self.redis_client:
            return []
        
        try:
            events_json = await self.redis_client.lrange(
                f"risk:events:{user_id}", 0, limit - 1
            )
            
            events = []
            for event_json in events_json:
                try:
                    event = json.loads(event_json)
                    events.append(event)
                except Exception as e:
                    self.logger.error(f"风控事件解析失败: {e}")
            
            return events
            
        except Exception as e:
            self.logger.error(f"获取用户风控事件失败: {e}")
            return []
    
    async def clear_user_frequency_limits(self, user_id: str):
        """
        清除用户频率限制计数器
        
        Args:
            user_id: 用户ID
        """
        if not self.redis_client:
            return
        
        try:
            # 获取所有频率限制键
            pattern = f"risk:frequency:{user_id}:*"
            keys = await self.redis_client.keys(pattern)
            
            if keys:
                await self.redis_client.delete(*keys)
                self.logger.info(f"清除用户频率限制: {user_id}")
                
        except Exception as e:
            self.logger.error(f"清除频率限制失败: {e}")